/*
 * File: serial.c
 *
 * Modified from Tim Smith's serial code by Peter Horak.
 * Purpose: recieve (binary) serial data from the Arduino
 */

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* For strlen */
#include <stdlib.h>  /* For calloc */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <dirent.h>  /* read directories and devices */
#include <signal.h>  /* For catching ^C interrupt */
#include <sys/time.h>/* For gettimeofday() */

#define BAUDRATE B230400
//#define BAUDRATE B115200

// For data-rate information
#define VERBOSE

const unsigned int sample_size = 200; // how many data lines to average before printing datarate statistics

static int done = 0;

// From Dirk Eddelbuettel on stackoverflow.com
void intHandler(int blank) {
    done = 1;
}

int main(int argc, char *argv[]){
    
    signal(SIGINT, intHandler);

    char * devPort = NULL;
    
    // Find Dev
    DIR *dir = opendir ("/dev");
    struct dirent *ent;
    while ((ent = readdir (dir))>0)
    {
        if ((strncmp("tty.usbmodem",ent->d_name,12)==0) ||
            (strncmp("tty.KeySerial",ent->d_name,12)==0) ||
            (strncmp("tty.USA",ent->d_name,7)==0))
        {
            devPort = ent->d_name;
            break;
        }
    }
    closedir (dir);
    if (devPort == NULL)
    {
        fprintf(stderr, "Failed to find a connected serial device\n");
        return 1; // failure
    }
    
    // Add "/dev/" to the port name
    char* fullPort = calloc(strlen(devPort) + 6,sizeof(char));
    sprintf(fullPort, "/dev/%s", devPort);
    fprintf(stderr, "Connecting to serial port \"%s\"\n",fullPort);
    
    int fd;
    fd = open(fullPort,(O_RDWR | O_NOCTTY | O_NDELAY));
    if (fd < 0)
    {
        fprintf(stderr, "Failed to connect to device.\n");
        return 1;
    }

    free(fullPort); // No longer needed
    
    //init port==================
    struct termios options;
    memset(&options,0,sizeof(options));
    // Set the baud rate
    cfsetispeed(&options, BAUDRATE);
    cfsetospeed(&options, BAUDRATE);
    // Enable the receiver and set local mode...
    options.c_cflag=CS8|CREAD|CLOCAL;
    options.c_cc[VMIN]=1;
    options.c_cc[VTIME]=5;
    // Set the new options for the port...
    tcsetattr(fd, TCSANOW, &options);
    //end init port==============
    
#ifdef VERBOSE
    struct timeval prev_time;
    struct timeval curr_time;
    unsigned long num_bytes = 0;
    unsigned int samples = 0;
#endif

    char result[2];
    int iRawCh;
    fcntl(fd, F_SETFL, FNDELAY); // don't block serial read
    fcntl(fileno(stdin), F_SETFL, FNDELAY); // don't hang on stdin
    
    // Interpret data as ASCII until the first sentinel is reached
    // Allow the user to send text (can have Arduino wait for a start command)
    char c = '\0';
    while (result[0] != '$' && result[0] != '#')
    {
        iRawCh = read(fd, result, 1);
        if (iRawCh > 0) fprintf(stderr, "%c", result[0]);
        
        c = getc(stdin);
        if (c != EOF) write(fd, &c, 1);
        
        if (done) return 0; // exit
    }
    
    while(!done){
        iRawCh = read(fd, result, 1);
        if (iRawCh>0)
        {
#ifdef VERBOSE
            ++num_bytes;
            if (result[0] == '$' || result[0] == '#')
            {
                ++samples;
                if (samples >= sample_size)
                {
                    gettimeofday(&curr_time, NULL);
                    double period = ((curr_time.tv_usec-prev_time.tv_usec)/1000000.0 + (curr_time.tv_sec-prev_time.tv_sec));
                    double sample_freq = 1.0/period;
                    prev_time = curr_time;

                    fprintf(stderr, "Datarate = %.2f Hz \tBytes/line = %.2f \tBitrate %.2f\n", sample_freq*samples, num_bytes/(double)samples, sample_freq*num_bytes*10); // 1 start bit + 8 bits of data + 1 stop bit
                    num_bytes = 0;
                    samples = 0;
                }
            }
#endif
            fprintf(stdout, "%c", result[0]);
            fflush(stdout);
        }
        else
            usleep(5000);
    }
    fflush(stdout);
    close(fd);
    return 0; // success
}
